package com.linkgie.galaxyframework.binaryserialize;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import com.linkgie.galaxyframework.binaryserialize.impl.BinaryTypesContext;

import utils.provider.Provider;
import utils.provider.ProviderManager;

/**
 * 二进制类型注册表；
 * 
 * @author huanghaiquan
 *
 */
public class BinaryTypeRegistry {

	// private  ProviderManager pm = new ProviderManager();

	// private  final BinaryTypeRegistry INSTANCE = new BinaryTypeRegistry();

	private  final BinaryTypesContext CONTEXT = new BinaryTypesContext();

	
	public static BinaryTypeRegistry create() {
		BinaryTypeRegistry registry = new BinaryTypeRegistry();
		registry.autoRegister();;
		return registry;
	}

	private void autoRegister() {
		ProviderManager pm = new ProviderManager();
		// 从当前类型的类加载器加载服务提供者；
		pm.installAllProviders(BinaryTypeProvider.class, BinaryProtocol.class.getClassLoader());
		// 从线程上下文类加载器加载服务提供者；（多次加载避免由于类加载器的原因产生遗漏，ProviderManager 内部会过滤重复加载）；
		pm.installAllProviders(BinaryTypeProvider.class, Thread.currentThread().getContextClassLoader());

		Iterable<Provider<BinaryTypeProvider>> providers = pm.getAllProviders(BinaryTypeProvider.class);
		List<BinaryTypeProvider> autoRegistrars = new ArrayList<BinaryTypeProvider>();
		for (Provider<BinaryTypeProvider> provider : providers) {
			autoRegistrars.add(provider.getService());
		}

		// 排序；
		autoRegistrars.sort(new Comparator<BinaryTypeProvider>() {
			@Override
			public int compare(BinaryTypeProvider o1, BinaryTypeProvider o2) {
				return o1.order() - o2.order();
			}
		});

		for (BinaryTypeProvider registrar : autoRegistrars) {
			registrar.initContext(this);
		}
	}

	private BinaryTypeRegistry() {
	}

	/**
	 * @param binaryType
	 * @return
	 */
	public BinaryCodec register(Class<?> binaryType) {
		return CONTEXT.register(binaryType);
	}
	
	public BinaryCodec getEncoder(Class<?> binaryType) {
		return CONTEXT.lookup(binaryType);
	}

	public BinaryCodec getEncoder(int binaryTypeCode, long version) {
		return CONTEXT.lookup(binaryTypeCode, version);
	}

	public <T> void registerBytesConverter(Class<T> binaryType, BytesConverter<T> converter) {
		CONTEXT.registerBytesConverter(binaryType, converter);
	}

}
